/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.security.auth.login;

import javax.security.auth.AuthPermission;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.util.Objects;

import sun.security.jca.GetInstance;

/**
 * A Configuration object is responsible for specifying which LoginModules
 * should be used for a particular application, and in what order the
 * LoginModules should be invoked.
 *
 * <p> A login configuration contains the following information.
 * Note that this example only represents the default syntax for the
 * {@code Configuration}.  Subclass implementations of this class
 * may implement alternative syntaxes and may retrieve the
 * {@code Configuration} from any source such as files, databases,
 * or servers.
 *
 * <pre>
 *      Name {
 *            ModuleClass  Flag    ModuleOptions;
 *            ModuleClass  Flag    ModuleOptions;
 *            ModuleClass  Flag    ModuleOptions;
 *      };
 *      Name {
 *            ModuleClass  Flag    ModuleOptions;
 *            ModuleClass  Flag    ModuleOptions;
 *      };
 *      other {
 *            ModuleClass  Flag    ModuleOptions;
 *            ModuleClass  Flag    ModuleOptions;
 *      };
 * </pre>
 *
 * <p> Each entry in the {@code Configuration} is indexed via an
 * application name, <i>Name</i>, and contains a list of
 * LoginModules configured for that application.  Each {@code LoginModule}
 * is specified via its fully qualified class name.
 * Authentication proceeds down the module list in the exact order specified.
 * If an application does not have a specific entry,
 * it defaults to the specific entry for "<i>other</i>".
 *
 * <p> The <i>Flag</i> value controls the overall behavior as authentication
 * proceeds down the stack.  The following represents a description of the
 * valid values for <i>Flag</i> and their respective semantics:
 *
 * <pre>
 *      1) Required     - The {@code LoginModule} is required to succeed.
 *                      If it succeeds or fails, authentication still continues
 *                      to proceed down the {@code LoginModule} list.
 *
 *      2) Requisite    - The {@code LoginModule} is required to succeed.
 *                      If it succeeds, authentication continues down the
 *                      {@code LoginModule} list.  If it fails,
 *                      control immediately returns to the application
 *                      (authentication does not proceed down the
 *                      {@code LoginModule} list).
 *
 *      3) Sufficient   - The {@code LoginModule} is not required to
 *                      succeed.  If it does succeed, control immediately
 *                      returns to the application (authentication does not
 *                      proceed down the {@code LoginModule} list).
 *                      If it fails, authentication continues down the
 *                      {@code LoginModule} list.
 *
 *      4) Optional     - The {@code LoginModule} is not required to
 *                      succeed.  If it succeeds or fails,
 *                      authentication still continues to proceed down the
 *                      {@code LoginModule} list.
 * </pre>
 *
 * <p> The overall authentication succeeds only if all <i>Required</i> and
 * <i>Requisite</i> LoginModules succeed.  If a <i>Sufficient</i>
 * {@code LoginModule} is configured and succeeds,
 * then only the <i>Required</i> and <i>Requisite</i> LoginModules prior to
 * that <i>Sufficient</i> {@code LoginModule} need to have succeeded for
 * the overall authentication to succeed. If no <i>Required</i> or
 * <i>Requisite</i> LoginModules are configured for an application,
 * then at least one <i>Sufficient</i> or <i>Optional</i>
 * {@code LoginModule} must succeed.
 *
 * <p> <i>ModuleOptions</i> is a space separated list of
 * {@code LoginModule}-specific values which are passed directly to
 * the underlying LoginModules.  Options are defined by the
 * {@code LoginModule} itself, and control the behavior within it.
 * For example, a {@code LoginModule} may define options to support
 * debugging/testing capabilities.  The correct way to specify options in the
 * {@code Configuration} is by using the following key-value pairing:
 * <i>debug="true"</i>.  The key and value should be separated by an
 * 'equals' symbol, and the value should be surrounded by double quotes.
 * If a String in the form, ${system.property}, occurs in the value,
 * it will be expanded to the value of the system property.
 * Note that there is no limit to the number of
 * options a {@code LoginModule} may define.
 *
 * <p> The following represents an example {@code Configuration} entry
 * based on the syntax above:
 *
 * <pre>
 * Login {
 *   com.sun.security.auth.module.UnixLoginModule required;
 *   com.sun.security.auth.module.Krb5LoginModule optional
 *                   useTicketCache="true"
 *                   ticketCache="${user.home}${/}tickets";
 * };
 * </pre>
 *
 * <p> This {@code Configuration} specifies that an application named,
 * "Login", requires users to first authenticate to the
 * <i>com.sun.security.auth.module.UnixLoginModule</i>, which is
 * required to succeed.  Even if the <i>UnixLoginModule</i>
 * authentication fails, the
 * <i>com.sun.security.auth.module.Krb5LoginModule</i>
 * still gets invoked.  This helps hide the source of failure.
 * Since the <i>Krb5LoginModule</i> is <i>Optional</i>, the overall
 * authentication succeeds only if the <i>UnixLoginModule</i>
 * (<i>Required</i>) succeeds.
 *
 * <p> Also note that the LoginModule-specific options,
 * <i>useTicketCache="true"</i> and
 * <i>ticketCache=${user.home}${/}tickets"</i>,
 * are passed to the <i>Krb5LoginModule</i>.
 * These options instruct the <i>Krb5LoginModule</i> to
 * use the ticket cache at the specified location.
 * The system properties, <i>user.home</i> and <i>/</i>
 * (file.separator), are expanded to their respective values.
 *
 * <p> There is only one Configuration object installed in the runtime at any
 * given time.  A Configuration object can be installed by calling the
 * {@code setConfiguration} method.  The installed Configuration object
 * can be obtained by calling the {@code getConfiguration} method.
 *
 * <p> If no Configuration object has been installed in the runtime, a call to
 * {@code getConfiguration} installs an instance of the default
 * Configuration implementation (a default subclass implementation of this
 * abstract class).
 * The default Configuration implementation can be changed by setting the value
 * of the {@code login.configuration.provider} security property to the fully
 * qualified name of the desired Configuration subclass implementation.
 *
 * <p> Application code can directly subclass Configuration to provide a custom
 * implementation.  In addition, an instance of a Configuration object can be
 * constructed by invoking one of the {@code getInstance} factory methods
 * with a standard type.  The default policy type is "JavaLoginConfig".
 * See the Configuration section in the <a href=
 * "{@docRoot}/../technotes/guides/security/StandardNames.html#Configuration">
 * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
 * for a list of standard Configuration types.
 *
 * @see javax.security.auth.login.LoginContext
 * @see java.security.Security security properties
 */
public abstract class Configuration {

  private static Configuration configuration;

  private final java.security.AccessControlContext acc =
      java.security.AccessController.getContext();

  private static void checkPermission(String type) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new AuthPermission
          ("createLoginConfiguration." + type));
    }
  }

  /**
   * Sole constructor.  (For invocation by subclass constructors, typically
   * implicit.)
   */
  protected Configuration() {
  }

  /**
   * Get the installed login Configuration.
   *
   * <p>
   *
   * @return the login Configuration.  If a Configuration object was set via the {@code
   * Configuration.setConfiguration} method, then that object is returned.  Otherwise, a default
   * Configuration object is returned.
   * @throws SecurityException if the caller does not have permission to retrieve the
   * Configuration.
   * @see #setConfiguration
   */
  public static Configuration getConfiguration() {

    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new AuthPermission("getLoginConfiguration"));
    }

    synchronized (Configuration.class) {
      if (configuration == null) {
        String config_class = null;
        config_class = AccessController.doPrivileged
            (new PrivilegedAction<String>() {
              public String run() {
                return java.security.Security.getProperty
                    ("login.configuration.provider");
              }
            });
        if (config_class == null) {
          config_class = "sun.security.provider.ConfigFile";
        }

        try {
          final String finalClass = config_class;
          Configuration untrustedImpl = AccessController.doPrivileged(
              new PrivilegedExceptionAction<Configuration>() {
                public Configuration run() throws ClassNotFoundException,
                    InstantiationException,
                    IllegalAccessException {
                  Class<? extends Configuration> implClass = Class.forName(
                      finalClass, false,
                      Thread.currentThread().getContextClassLoader()
                  ).asSubclass(Configuration.class);
                  return implClass.newInstance();
                }
              });
          AccessController.doPrivileged(
              new PrivilegedExceptionAction<Void>() {
                public Void run() {
                  setConfiguration(untrustedImpl);
                  return null;
                }
              }, Objects.requireNonNull(untrustedImpl.acc)
          );
        } catch (PrivilegedActionException e) {
          Exception ee = e.getException();
          if (ee instanceof InstantiationException) {
            throw (SecurityException) new
                SecurityException
                ("Configuration error:" +
                    ee.getCause().getMessage() +
                    "\n").initCause(ee.getCause());
          } else {
            throw (SecurityException) new
                SecurityException
                ("Configuration error: " +
                    ee.toString() +
                    "\n").initCause(ee);
          }
        }
      }
      return configuration;
    }
  }

  /**
   * Set the login {@code Configuration}.
   *
   * <p>
   *
   * @param configuration the new {@code Configuration}
   * @throws SecurityException if the current thread does not have Permission to set the {@code
   * Configuration}.
   * @see #getConfiguration
   */
  public static void setConfiguration(Configuration configuration) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new AuthPermission("setLoginConfiguration"));
    }
    Configuration.configuration = configuration;
  }

  /**
   * Returns a Configuration object of the specified type.
   *
   * <p> This method traverses the list of registered security providers,
   * starting with the most preferred Provider.
   * A new Configuration object encapsulating the
   * ConfigurationSpi implementation from the first
   * Provider that supports the specified type is returned.
   *
   * <p> Note that the list of registered providers may be retrieved via
   * the {@link Security#getProviders() Security.getProviders()} method.
   *
   * @param type the specified Configuration type.  See the Configuration section in the <a href=
   * "{@docRoot}/../technotes/guides/security/StandardNames.html#Configuration"> Java Cryptography
   * Architecture Standard Algorithm Name Documentation</a> for a list of standard Configuration
   * types.
   * @param params parameters for the Configuration, which may be null.
   * @return the new Configuration object.
   * @throws SecurityException if the caller does not have permission to get a Configuration
   * instance for the specified type.
   * @throws NullPointerException if the specified type is null.
   * @throws IllegalArgumentException if the specified parameters are not understood by the
   * ConfigurationSpi implementation from the selected Provider.
   * @throws NoSuchAlgorithmException if no Provider supports a ConfigurationSpi implementation for
   * the specified type.
   * @see Provider
   * @since 1.6
   */
  public static Configuration getInstance(String type,
      Configuration.Parameters params)
      throws NoSuchAlgorithmException {

    checkPermission(type);
    try {
      GetInstance.Instance instance = GetInstance.getInstance
          ("Configuration",
              ConfigurationSpi.class,
              type,
              params);
      return new ConfigDelegate((ConfigurationSpi) instance.impl,
          instance.provider,
          type,
          params);
    } catch (NoSuchAlgorithmException nsae) {
      return handleException(nsae);
    }
  }

  /**
   * Returns a Configuration object of the specified type.
   *
   * <p> A new Configuration object encapsulating the
   * ConfigurationSpi implementation from the specified provider
   * is returned.   The specified provider must be registered
   * in the provider list.
   *
   * <p> Note that the list of registered providers may be retrieved via
   * the {@link Security#getProviders() Security.getProviders()} method.
   *
   * @param type the specified Configuration type.  See the Configuration section in the <a href=
   * "{@docRoot}/../technotes/guides/security/StandardNames.html#Configuration"> Java Cryptography
   * Architecture Standard Algorithm Name Documentation</a> for a list of standard Configuration
   * types.
   * @param params parameters for the Configuration, which may be null.
   * @param provider the provider.
   * @return the new Configuration object.
   * @throws SecurityException if the caller does not have permission to get a Configuration
   * instance for the specified type.
   * @throws NullPointerException if the specified type is null.
   * @throws IllegalArgumentException if the specified provider is null or empty, or if the
   * specified parameters are not understood by the ConfigurationSpi implementation from the
   * specified provider.
   * @throws NoSuchProviderException if the specified provider is not registered in the security
   * provider list.
   * @throws NoSuchAlgorithmException if the specified provider does not support a ConfigurationSpi
   * implementation for the specified type.
   * @see Provider
   * @since 1.6
   */
  public static Configuration getInstance(String type,
      Configuration.Parameters params,
      String provider)
      throws NoSuchProviderException, NoSuchAlgorithmException {

    if (provider == null || provider.length() == 0) {
      throw new IllegalArgumentException("missing provider");
    }

    checkPermission(type);
    try {
      GetInstance.Instance instance = GetInstance.getInstance
          ("Configuration",
              ConfigurationSpi.class,
              type,
              params,
              provider);
      return new ConfigDelegate((ConfigurationSpi) instance.impl,
          instance.provider,
          type,
          params);
    } catch (NoSuchAlgorithmException nsae) {
      return handleException(nsae);
    }
  }

  /**
   * Returns a Configuration object of the specified type.
   *
   * <p> A new Configuration object encapsulating the
   * ConfigurationSpi implementation from the specified Provider
   * object is returned.  Note that the specified Provider object
   * does not have to be registered in the provider list.
   *
   * @param type the specified Configuration type.  See the Configuration section in the <a href=
   * "{@docRoot}/../technotes/guides/security/StandardNames.html#Configuration"> Java Cryptography
   * Architecture Standard Algorithm Name Documentation</a> for a list of standard Configuration
   * types.
   * @param params parameters for the Configuration, which may be null.
   * @param provider the Provider.
   * @return the new Configuration object.
   * @throws SecurityException if the caller does not have permission to get a Configuration
   * instance for the specified type.
   * @throws NullPointerException if the specified type is null.
   * @throws IllegalArgumentException if the specified Provider is null, or if the specified
   * parameters are not understood by the ConfigurationSpi implementation from the specified
   * Provider.
   * @throws NoSuchAlgorithmException if the specified Provider does not support a ConfigurationSpi
   * implementation for the specified type.
   * @see Provider
   * @since 1.6
   */
  public static Configuration getInstance(String type,
      Configuration.Parameters params,
      Provider provider)
      throws NoSuchAlgorithmException {

    if (provider == null) {
      throw new IllegalArgumentException("missing provider");
    }

    checkPermission(type);
    try {
      GetInstance.Instance instance = GetInstance.getInstance
          ("Configuration",
              ConfigurationSpi.class,
              type,
              params,
              provider);
      return new ConfigDelegate((ConfigurationSpi) instance.impl,
          instance.provider,
          type,
          params);
    } catch (NoSuchAlgorithmException nsae) {
      return handleException(nsae);
    }
  }

  private static Configuration handleException(NoSuchAlgorithmException nsae)
      throws NoSuchAlgorithmException {
    Throwable cause = nsae.getCause();
    if (cause instanceof IllegalArgumentException) {
      throw (IllegalArgumentException) cause;
    }
    throw nsae;
  }

  /**
   * Return the Provider of this Configuration.
   *
   * <p> This Configuration instance will only have a Provider if it
   * was obtained via a call to {@code Configuration.getInstance}.
   * Otherwise this method returns null.
   *
   * @return the Provider of this Configuration, or null.
   * @since 1.6
   */
  public Provider getProvider() {
    return null;
  }

  /**
   * Return the type of this Configuration.
   *
   * <p> This Configuration instance will only have a type if it
   * was obtained via a call to {@code Configuration.getInstance}.
   * Otherwise this method returns null.
   *
   * @return the type of this Configuration, or null.
   * @since 1.6
   */
  public String getType() {
    return null;
  }

  /**
   * Return Configuration parameters.
   *
   * <p> This Configuration instance will only have parameters if it
   * was obtained via a call to {@code Configuration.getInstance}.
   * Otherwise this method returns null.
   *
   * @return Configuration parameters, or null.
   * @since 1.6
   */
  public Configuration.Parameters getParameters() {
    return null;
  }

  /**
   * Retrieve the AppConfigurationEntries for the specified <i>name</i>
   * from this Configuration.
   *
   * <p>
   *
   * @param name the name used to index the Configuration.
   * @return an array of AppConfigurationEntries for the specified <i>name</i> from this
   * Configuration, or null if there are no entries for the specified <i>name</i>
   */
  public abstract AppConfigurationEntry[] getAppConfigurationEntry
  (String name);

  /**
   * Refresh and reload the Configuration.
   *
   * <p> This method causes this Configuration object to refresh/reload its
   * contents in an implementation-dependent manner.
   * For example, if this Configuration object stores its entries in a file,
   * calling {@code refresh} may cause the file to be re-read.
   *
   * <p> The default implementation of this method does nothing.
   * This method should be overridden if a refresh operation is supported
   * by the implementation.
   *
   * @throws SecurityException if the caller does not have permission to refresh its Configuration.
   */
  public void refresh() {
  }

  /**
   * This subclass is returned by the getInstance calls.  All Configuration
   * calls are delegated to the underlying ConfigurationSpi.
   */
  private static class ConfigDelegate extends Configuration {

    private ConfigurationSpi spi;
    private Provider p;
    private String type;
    private Configuration.Parameters params;

    private ConfigDelegate(ConfigurationSpi spi, Provider p,
        String type, Configuration.Parameters params) {
      this.spi = spi;
      this.p = p;
      this.type = type;
      this.params = params;
    }

    public String getType() {
      return type;
    }

    public Configuration.Parameters getParameters() {
      return params;
    }

    public Provider getProvider() {
      return p;
    }

    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
      return spi.engineGetAppConfigurationEntry(name);
    }

    public void refresh() {
      spi.engineRefresh();
    }
  }

  /**
   * This represents a marker interface for Configuration parameters.
   *
   * @since 1.6
   */
  public static interface Parameters {

  }
}
